package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.heima.apis.admin.IAdminFeign;
import com.heima.article.service.ApArticleService;
import com.heima.article.service.HotArticleService;
import com.heima.common.constants.article.ArticleConstants;
import com.heima.model.admin.pojos.AdChannel;
import com.heima.model.article.mess.ArticleVisitStreamMess;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.vo.HotArticleVo;
import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class HotArticleServiceImpl implements HotArticleService {

    @Autowired
    private ApArticleService apArticleService;

    @Override
    public void computeScore() {
        //1.查询最近5天的所有文章
        String beginDate = DateTime.now().minusDays(5).toString("yyyy-MM-dd 00:00:00");
        List<ApArticle> apArticleList = apArticleService.list(Wrappers.<ApArticle>lambdaQuery().gt(ApArticle::getPublishTime, beginDate));

        //2.计算所有文章的分值并得到带分值的文章列表
        List<HotArticleVo> hotArticleVoList = computeAllScore(apArticleList);

        //3.为所有频道排序文章列表并存redis
        saveRedis(hotArticleVoList);
    }

    @Autowired
    private IAdminFeign adminFeign;

    /**
     * 1. 找出每个频道的文章列表数据分值top30，并存redis
     * 2. 找出推荐频道的文章列表数据分值top30，并存redis
     * @param hotArticleVoList
     */
    private void saveRedis(List<HotArticleVo> hotArticleVoList) {

        //1. 找出每个频道的文章列表数据分值top30，并存redis
        List<AdChannel> adChannelList = adminFeign.listAll();
        for (AdChannel adChannel : adChannelList) {
            //获取每个频道对应的文章列表
            List<HotArticleVo> articleVoList = hotArticleVoList.stream().filter(x -> x.getChannelId().equals(adChannel.getId())).collect(Collectors.toList());
            //找出文章列表分值top30数据，并存入redis
            sortAndSaveRedis(articleVoList, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + adChannel.getId());
        }

        // 2. 找出推荐频道的文章列表数据分值top30，并存redis
        sortAndSaveRedis(hotArticleVoList, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + ArticleConstants.DEFAULT_TAG);
    }


    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    //获取文章列表分值top30,将数据存入redis中
    private void sortAndSaveRedis(List<HotArticleVo> articleVoList, String redisKey) {
        if(articleVoList!=null && articleVoList.size()>0){
            //1.按照所有文章的分值进行倒序排序
            articleVoList = articleVoList.stream().sorted(Comparator.comparing(HotArticleVo::getScore).reversed()).collect(Collectors.toList());

            //2.判断如果了列表超过30条，则取前30条
            if(articleVoList.size()>30){
                articleVoList = articleVoList.subList(0,30);
            }

            //3.将文章列表转为JSON存入redis
            stringRedisTemplate.opsForValue().set(redisKey, JSON.toJSONString(articleVoList));
        }
    }

    //计算所有文章分值
    private List<HotArticleVo> computeAllScore(List<ApArticle> apArticleList) {
        List<HotArticleVo> hotArticleVoList = new ArrayList<>();
        if(apArticleList!=null && apArticleList.size()>0){
            for (ApArticle apArticle : apArticleList) {
                int score = computeSingleArticle(apArticle); //为每一个文章计算分值
                HotArticleVo hotArticleVo = new HotArticleVo();
                BeanUtils.copyProperties(apArticle,hotArticleVo);
                hotArticleVo.setScore(score);
                hotArticleVoList.add(hotArticleVo);
            }
        }
        return hotArticleVoList;
    }

    //每条文章分值
    private int computeSingleArticle(ApArticle apArticle) {
        int score = 0;
        if(apArticle.getViews()>0){
            score += apArticle.getViews(); //累加阅读分值
        }
        if(apArticle.getLikes()>0){
            score += apArticle.getLikes() * ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT; //累加点赞分值
        }
        if(apArticle.getComment()>0){
            score += apArticle.getComment() * ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT ; //累加评论分值
        }
        if(apArticle.getCollection()>0){
            score += apArticle.getCollection() * ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT; //累加收藏分值
        }
        return score;
    }



    @Override
    public void recomputeScore(ArticleVisitStreamMess articleVisitStreamMess) {
        //1.更新文章行为数据
        ApArticle apArticle = apArticleService.getById(articleVisitStreamMess.getArticleId());
        //累加点赞数
        if(articleVisitStreamMess.getLike()!=null && articleVisitStreamMess.getLike()>0){
            apArticle.setLikes(apArticle.getLikes() + articleVisitStreamMess.getLike());
        }
        //累加阅读数
        if(articleVisitStreamMess.getView()!=null && articleVisitStreamMess.getView()>0){
            apArticle.setViews(apArticle.getViews() + articleVisitStreamMess.getView());
        }
        //累加评论数
        if(articleVisitStreamMess.getComment()!=null && articleVisitStreamMess.getComment()>0){
            apArticle.setComment(apArticle.getComment() + articleVisitStreamMess.getComment());
        }
        //累加收藏数
        if(articleVisitStreamMess.getCollect()!=null && articleVisitStreamMess.getCollect()>0){
            apArticle.setCollection(apArticle.getCollection() + articleVisitStreamMess.getCollect());
        }
        apArticleService.updateById(apArticle);

        //2.重新计算文章分值
        int score = computeSingleArticle(apArticle) * 3;

        //3.与redis中数据对比并更新（普通频道）
        saveRedisMore(apArticle, score, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + apArticle.getChannelId());

        //4.与redis中数据对比并更新（推荐频道）
        saveRedisMore(apArticle, score, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + ArticleConstants.DEFAULT_TAG);
    }

    
    
    //将当前重新计算分值的文章与redis中的文章列表中数据对比，并更新redis
    private void saveRedisMore(ApArticle apArticle, int score, String redisKey) {
        //1.根据key从redis获取文章列表
        String articleListJson = stringRedisTemplate.opsForValue().get(redisKey);
        List<HotArticleVo> hotArticleVoList = JSON.parseArray(articleListJson, HotArticleVo.class);
        if(hotArticleVoList!=null && hotArticleVoList.size()>0){
            //如果redis中有文章列表，那么就要找到改文章
            boolean isNotExists = true; //默认不存在

            for (HotArticleVo hotArticleVo : hotArticleVoList) {

                //如果找到改文章，那么将改文章的最新值替换掉
                if(hotArticleVo.getId().equals(apArticle.getId())){
                    BeanUtils.copyProperties(apArticle,hotArticleVo);
                    hotArticleVo.setScore(score);
                    isNotExists = false;
                    break;
                }
            }


            //如果没有找到改文章，就判断文章列表数量
            if(isNotExists){
                if(hotArticleVoList.size()>=30){
                    //如果列表数量大于等于30，那么当前文章与列表中最后一条文章分值进行对比，如果分值大于他，就替换
                    HotArticleVo hotArticleVoLast = hotArticleVoList.get(hotArticleVoList.size() - 1);
                    if(score> hotArticleVoLast.getScore()){
                        hotArticleVoList.remove(hotArticleVoLast);// 将最后一条文章移除

                        //将当前文章存入redis缓存
                        HotArticleVo hotArticleVo = new HotArticleVo();
                        BeanUtils.copyProperties(apArticle,hotArticleVo);
                        hotArticleVo.setScore(score);
                        hotArticleVoList.add(hotArticleVo);
                    }

                } else {
                    //如果列表数量不足30，直接将改文章存入到redis缓存
                    HotArticleVo hotArticleVo = new HotArticleVo();
                    BeanUtils.copyProperties(apArticle,hotArticleVo);
                    hotArticleVo.setScore(score);
                    hotArticleVoList.add(hotArticleVo);
                }
            }



        } else {
            //如果redis中没有文章数据，那么直接将当前文章保存到redis缓存中
            hotArticleVoList = new ArrayList<>();
            HotArticleVo hotArticleVo = new HotArticleVo();
            BeanUtils.copyProperties(apArticle,hotArticleVo);
            hotArticleVo.setScore(score);
            hotArticleVoList.add(hotArticleVo);
        }

        //处理文章列表的排序与保存
        hotArticleVoList = hotArticleVoList.stream().sorted(Comparator.comparing(HotArticleVo::getScore).reversed()).collect(Collectors.toList());

        stringRedisTemplate.opsForValue().set(redisKey, JSON.toJSONString(hotArticleVoList));

    }
}
